/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.kittinunf.fuel.stetho;

import com.facebook.stetho.urlconnection.ByteArrayRequestEntity;
import com.facebook.stetho.urlconnection.StethoURLConnectionManager;
import com.github.kittinunf.fuel.core.Client;
import com.github.kittinunf.fuel.core.Request;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import kotlin.jvm.JvmClassMappingKt;

import kotlin.reflect.KClass;


/**
 * @since 2021-05-26
 */
public class StethoHook implements Client.Hook {
    private ConcurrentHashMap<Object, StethoURLConnectionManager> stethoCache;
    private String friendlyName;

    private UUID uuid;

    /**
     * getFriendlyName
     *
     * @return String
     */
    public String getFriendlyName() {
        return friendlyName;
    }

    /**
     * getStethoCache
     *
     * @return ConcurrentHashMap<Object, StethoURLConnectionManager>
     */
    public ConcurrentHashMap<Object, StethoURLConnectionManager> getStethoCache() {
        return stethoCache;
    }

    /**
     * StethoHook
     *
     * @param friendlyName friendlyName
     */
    public StethoHook(String friendlyName) {
        this.friendlyName = friendlyName;
        this.stethoCache = new ConcurrentHashMap();
    }

    /**
     * getKClass
     *
     * @param cls cls
     * @param <T> t
     * @return KClass<T> t
     */
    public <T> KClass<T> getKClass(Class<T> cls) {
        return JvmClassMappingKt.getKotlinClass(cls);
    }

    /**
     * preConnect
     *
     * @param connection connection
     * @param request request
     */
    @Override
    public void preConnect(HttpURLConnection connection, Request request) {
        if (connection == null) {
            return;
        }
        if (request == null) {
            return;
        }
        uuid = UUID.randomUUID();
        request.tag(uuid);

        StethoURLConnectionManager stetho = null;
        stetho = this.stethoCache.getOrDefault(uuid, null);
        if (stetho == null) {
            stetho = new StethoURLConnectionManager(this.friendlyName);
            this.stethoCache.put(uuid, stetho);
        }
        stetho.preConnect(connection, new ByteArrayRequestEntity(request.getBody().toByteArray()));
    }

    @Override
    public void postConnect(Request request) {
        if (request == null) {
            return;
        }
        StethoURLConnectionManager manager = stethoCache.getOrDefault(request.getTag(getKClass(uuid.getClass())), null);
        try {
            manager.postConnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public InputStream interpretResponseStream(Request request, InputStream inputStream) throws NullPointerException {
        if (request == null) {
            Optional<InputStream> optionalUnit = Optional.empty();
            return optionalUnit.get();
        }

        StethoURLConnectionManager manager = stethoCache.getOrDefault(request.getTag(getKClass(uuid.getClass())), null);
        InputStream inputStreamOne = null;
        if (manager != null) {
            inputStreamOne = manager.interpretResponseStream(inputStream);
        }

        stethoCache.remove(request.getTag(getKClass(uuid.getClass())));
        if (inputStreamOne != null) {
            return inputStreamOne;
        } else {
            return inputStream;
        }
    }

    @Override
    public void httpExchangeFailed(Request request, IOException exception) throws NullPointerException {
        if (exception == null) {
            return;
        }
        if (request == null) {
            return;
        }
        StethoURLConnectionManager manager = stethoCache.getOrDefault(request.getTag(getKClass(uuid.getClass())), null);
        if (manager != null) {
            manager.httpExchangeFailed(exception);
        }
        stethoCache.remove(request.getTag(getKClass(uuid.getClass())));
    }
}